package com.jxy.tool;

import com.jxy.bean.ClassFile;
import com.jxy.bean.FieldInfo;
import com.jxy.bean.MethodInfo;
import com.jxy.bean.attribute.AttibuteCodeExceptionInfo;
import com.jxy.bean.attribute.Attribute;
import com.jxy.bean.attribute.AttributeBootstrapMethod;
import com.jxy.bean.attribute.AttributeLineNumber;
import com.jxy.bean.constant.*;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;

public class Tool {

    public static final HashMap<Integer, String> class_access_flags = new HashMap<Integer, String>() {{
        put(0x0001, "ACC_PUBLIC");
        put(0x0010, "ACC_FINAL");
        put(0x0020, "ACC_SUPER");
        put(0x0200, "ACC_INTERFACE");
        put(0x0400, "ACC_ABSTRACT");
        put(0x1000, "ACC_SYNTHETIC");
        put(0x2000, "ACC_ANNOTATION");
        put(0x4000, "ACC_ENUM");
        put(0x8000, "ACC_MODULE");
    }};

    public static final HashMap<Integer, String> field_access_flags = new HashMap<Integer, String>() {{
        put(0x0001, "ACC_PUBLIC");
        put(0x0002, "ACC_PRIVATE");
        put(0x0004, "ACC_PROTECTED");
        put(0x0008, "ACC_STATIC");
        put(0x0010, "ACC_FINAL");
        put(0x0040, "ACC_VOLATILE");
        put(0x0080, "ACC_TRANSIENT");
        put(0x1000, "ACC_SYNTHETIC");
        put(0x4000, "ACC_ENUM");
    }};

    public static final HashMap<Integer, String> method_access_flags = new HashMap<Integer, String>() {{
        put(0x0001, "ACC_PUBLIC");
        put(0x0002, "ACC_PRIVATE");
        put(0x0004, "ACC_PROTECTED");
        put(0x0008, "ACC_STATIC");
        put(0x0010, "ACC_FINAL");
        put(0x0020, "ACC_SYNCHRONIZED");
        put(0x0040, "ACC_BRIDGE");
        put(0x0080, "ACC_VARARGS");
        put(0x0100, "ACC_NATIVE");
        put(0x0400, "ACC_ABSTRACT");
        put(0x0800, "ACC_STRICT");
        put(0x1000, "ACC_SYNTHETIC");
    }};

    public static byte[] readBytes(DataInputStream dis, int length) {
        byte[] data = new byte[length];
        try {
            dis.read(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

    public static float readFloat(DataInputStream dis) {
        float f = 0;
        try {
            f = dis.readFloat();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return f;
    }

    public static long readLong(DataInputStream dis) {
        long l = 0;
        try {
            l = dis.readLong();
            // int hight_bytes=readU4(dis);
            // int low_bytes=readU4(dis);
            // long l=((long)hight_bytes<<32)+low_bytes;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return l;
    }

    public static double readDouble(DataInputStream dis) {
        double d = 0;
        try {
            d = dis.readDouble();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return d;
    }

    public static int readU1(DataInputStream dis) {
        int i = -1;
        try {
            i = dis.readUnsignedByte();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return i;
    }

    public static int readU2(DataInputStream dis) {
        int i = -1;
        try {
            i = dis.readUnsignedShort();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return i;
    }

    public static int readU4(DataInputStream dis) {
        int i = -1;
        try {
            i = dis.readInt();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return i;
    }

    public static String analyAccessFlag(HashMap<Integer, String> access_flags, int flags) {
        StringBuffer des = new StringBuffer();
        int all = 0;
        for (Integer key : access_flags.keySet()) {
            all |= key;
            if ((flags & key) != 0) {
                des.append(access_flags.get(key)).append(" ");
            }
        }


        if ((all | flags) != all) {
            des.append("? ");
        }

        if (des.length() > 0) {
            des.setLength(des.length() - 1);
        }

        return des.toString();
    }

    public static byte[] readMagic(DataInputStream dis) {
        byte[] data = readBytes(dis, 4);
        return data;
    }

    public static String readVersion(DataInputStream dis) {
        //次要的
        int MinorVersion = readU2(dis);
        //主要的
        int MajorVersion = readU2(dis);
        return MajorVersion + "." + MinorVersion;
    }

    public static ConstantPool[] readConstantPool(DataInputStream dis, ClassFile cf) {
        //读取常量池计数器
        int constant_pool_count = readU2(dis);
        ConstantPool[] pools = new ConstantPool[constant_pool_count];
        //索引0使用
        pools[0] = null;
        int tag;
        for (int i = 1; i < constant_pool_count; i++) {
            tag = readU1(dis);
            switch (tag) {
                case 1://CONSTANT_Utf8_info
                    Utf8Info utf8 = new Utf8Info(dis, cf);
                    pools[i] = utf8;
                    break;
                case 3://CONSTANT_Integer_info
                    IntegerInfo integerInfo = new IntegerInfo(dis, cf);
                    pools[i] = integerInfo;
                    break;
                case 4://CONSTANT_Float_info
                    FloatInfo floatInfo = new FloatInfo(dis, cf);
                    pools[i] = floatInfo;
                    break;
                case 5://CONSTANT_Long_info
                    LongInfo longInfo = new LongInfo(dis, cf);
                    pools[i] = longInfo;
                    //需要占位索引
                    i++;
                    longInfo = new LongInfo(cf);
                    pools[i] = longInfo;
                    break;
                case 6://CONSTANT_Double_info
                    DoubleInfo doubleInfo = new DoubleInfo(dis, cf);
                    pools[i] = doubleInfo;
                    //需要占位索引
                    i++;
                    doubleInfo = new DoubleInfo(cf);
                    pools[i] = doubleInfo;
                    break;
                case 7://CONSTANT_Class_info
                    ClassInfo classInfo = new ClassInfo(dis, cf);
                    pools[i] = classInfo;
                    break;
                case 8://CONSTANT_String_info
                    StringInfo stringInfo = new StringInfo(dis, cf);
                    pools[i] = stringInfo;
                    break;
                case 9://CONSTANT_Fieldref_info
                    FieldrefInfo fieldrefInfo = new FieldrefInfo(dis, cf);
                    pools[i] = fieldrefInfo;
                    break;
                case 10://CONSTANT_Methodref_info
                    MethodrefInfo methodrefInfo = new MethodrefInfo(dis, cf);
                    pools[i] = methodrefInfo;
                    break;
                case 11://CONSTANT_InterfaceMethodref_info
                    InterfaceMethodrefInfo interfaceMethodrefInfo = new InterfaceMethodrefInfo(dis, cf);
                    pools[i] = interfaceMethodrefInfo;
                    break;
                case 12://CONSTANT_NameAndType_info
                    NameAndTypeInfo nameAndTypeInfo = new NameAndTypeInfo(dis, cf);
                    pools[i] = nameAndTypeInfo;
                    break;
                case 15://CONSTANT_MethodHandle_info
                    MethodHandleInfo methodHandleInfo = new MethodHandleInfo(dis, cf);
                    pools[i] = methodHandleInfo;
                    break;
                case 16://CONSTANT_MethodType_info
                    MethodTypeInfo methodTypeInfo = new MethodTypeInfo(dis, cf);
                    pools[i] = methodTypeInfo;
                    break;
                case 17://CONSTANT_Dynamic_info
                    DynamicInfo dynamicInfo = new DynamicInfo(dis, cf);
                    pools[i] = dynamicInfo;
                    break;
                case 18://CONSTANT_InvokeDynamic_info
                    InvokeDynamicInfo invokeDynamicInfo = new InvokeDynamicInfo(dis, cf);
                    pools[i] = invokeDynamicInfo;
                    break;
                case 19://CONSTANT_Module_info
                    ModuleInfo moduleInfo = new ModuleInfo(dis, cf);
                    pools[i] = moduleInfo;
                    break;
                case 20://CONSTANT_Package_info
                    PackageInfo packageInfo = new PackageInfo(dis, cf);
                    pools[i] = packageInfo;
                    break;
                default:
                    throw new RuntimeException("constant_pool_count:" + constant_pool_count + ";constant_pool_index:" + i + ";Unknow tag:" + tag);
            }
        }
        return pools;
    }

    public static int[] readInterface(DataInputStream dis) {
        int class_index = 0;
        int interfaces_count = readU2(dis);
        int[] interfaces = new int[interfaces_count];

        for (int i = 0; i < interfaces_count; i++) {
            class_index = readU2(dis);
            interfaces[i] = class_index;
        }

        return interfaces;
    }

    public static FieldInfo[] readField(DataInputStream dis, ClassFile cf) {
        int fields_count = readU2(dis);
        FieldInfo[] fields = new FieldInfo[fields_count];
        for (int i = 0; i < fields_count; i++) {
            FieldInfo field = new FieldInfo(dis, cf);
            fields[i] = field;
        }
        return fields;
    }

    public static MethodInfo[] readMethod(DataInputStream dis, ClassFile cf) {
        int methods_count = readU2(dis);
        MethodInfo[] methods = new MethodInfo[methods_count];
        for (int i = 0; i < methods_count; i++) {
            MethodInfo method = new MethodInfo(dis, cf);
            methods[i] = method;
        }
        return methods;
    }

    public static Attribute[] readAttribute(DataInputStream dis, ClassFile cf) {
        int attributes_count = readU2(dis);
        Attribute[] attributes = new Attribute[attributes_count];
        for (int i = 0; i < attributes_count; i++) {
            Attribute attribute = new Attribute(dis, cf);
            attributes[i] = attribute;
        }
        return attributes;
    }

    public static AttibuteCodeExceptionInfo[] readException(DataInputStream dis, ClassFile cf) {
        int number_of_exceptions = readU2(dis);
        AttibuteCodeExceptionInfo[] exceptionInfos = new AttibuteCodeExceptionInfo[number_of_exceptions];
        for (int i = 0; i < number_of_exceptions; i++) {
            AttibuteCodeExceptionInfo exceptionInfo = new AttibuteCodeExceptionInfo(dis, cf);
            exceptionInfos[i] = exceptionInfo;
        }
        return exceptionInfos;
    }

    public static AttributeBootstrapMethod[] AttributeBootstrapMethod(DataInputStream dis, ClassFile cf) {
        int num_bootstrap_methods = readU2(dis);
        AttributeBootstrapMethod[] bootstrapMethods = new AttributeBootstrapMethod[num_bootstrap_methods];
        for (int i = 0; i < num_bootstrap_methods; i++) {
            AttributeBootstrapMethod bootstrapMethod = new AttributeBootstrapMethod(dis, cf);
            bootstrapMethods[i] = bootstrapMethod;
        }
        return bootstrapMethods;
    }

    public static AttributeLineNumber[] AttributeLineNumber(DataInputStream dis, ClassFile cf) {
        int line_number_table_length = readU2(dis);
        AttributeLineNumber[] attributeLineNumbers = new AttributeLineNumber[line_number_table_length];
        for (int i = 0; i < line_number_table_length; i++) {
            AttributeLineNumber attributeLineNumber = new AttributeLineNumber(dis, cf);
            attributeLineNumbers[i] = attributeLineNumber;
        }
        return attributeLineNumbers;
    }

}
